package cn.maihe.elg.operation.supports.bank.service;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.maihe.elg.operation.repository.entity.SpdBankNotice;
import cn.maihe.elg.operation.service.order.AcceptOrderInfoService;
import cn.maihe.elg.operation.service.order.SpdBankNoticeService;
import cn.maihe.elg.operation.supports.bank.BaseSpdBankTransService;
import cn.maihe.elg.operation.supports.bank.dto.*;
import cn.maihe.elg.operation.supports.bank.enums.SpdbMethod;
import cn.maihe.elg.operation.utils.ElgLogger;
import cn.maihe.elg.operation.utils.IdGenerate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @Description 接口统一识别码 OPC103100201000300008900
 * @Author Alan
 */
@Service
@Slf4j
public class SpdBankBiSafeAccountDetailService extends BaseSpdBankTransService<AccountDetailReqDTO, AccountDetailRespDTO> {

    @Resource
    private SpdBankNoticeService spdBankNoticeService;
    @Resource
    private AcceptOrderInfoService acceptOrderInfoService;

    private AtomicBoolean running = new AtomicBoolean(false);

    @Override
    protected SpdbMethod spdbMethod() {
        return SpdbMethod.BiSafeAccountDetail;
    }

    @Scheduled(cron = "${cn.maihe.elg.bank.spd-bank.bi-safe.getAccountDetailCore}")
    public void scheduleBiSafeAccountDetail() {

        if (!"1".equals(spdBankBiSafeConfig.getOpenFlag())) {
            log.info("当前已关闭浦发银企直连功能，暂无需开启定时拉取账户明细任务");
            return;
        }

        if (running.compareAndSet(false, true)) {
            long start = System.currentTimeMillis();
            ElgLogger.info(log, l -> l.info("定时拉取账户明细开始>>>"));
            try {
                //若是凌晨00:30前则需要同步昨天的记录，以防行内来账较迟
                LocalDate startDate = LocalDate.now();
                if (LocalTime.now().isBefore(LocalTime.of(0, 30, 0))) {
                    startDate = startDate.minusDays(1);
                }

                this.getAccountDetail(1, startDate, LocalDate.now());
            } catch (Exception e) {
                ElgLogger.error(log, l -> l.error("定时拉取账户明细异常:{}", e.getMessage(), e));
            } finally {
                running.set(false);
                long end = System.currentTimeMillis();
                ElgLogger.info(log, l -> l.info("定时拉取账户明细完成<<<耗时[" + (end - start) / 1000 + "S]"));
            }
        } else {
            ElgLogger.info(log, l -> l.info("定时拉取账户明细开始>>>已存在同步拉取任务，等待下一个定时任务重跑"));
        }
    }

    public boolean getAccountDetail(int startRow, LocalDate startDate, LocalDate endDate) {
        Integer mixRsltNum = 50;
        AccountDetailReqDTO accountDetailReqDTO = new AccountDetailReqDTO();
        //请求头head
        BaseSpdBankReqHead baseSpdBankHead = new BaseSpdBankReqHead();
        baseSpdBankHead.setTransCode(SpdbMethod.BiSafeAccountDetail.getMethodId());
        baseSpdBankHead.setSignFlag(SpdbMethod.BiSafeAccountDetail.getEncryption() ? "1" : "0");
        baseSpdBankHead.setMasterID(spdBankBiSafeConfig.getMasterID());
        baseSpdBankHead.setPacketID(IdGenerate.getIdStr(20));
        baseSpdBankHead.setTimeStamp(DateTime.now().toString(DatePattern.NORM_DATETIME_FORMAT));
        //请求体body
        AccountDetailReqBody accountDetailReqBody = new AccountDetailReqBody();
        accountDetailReqBody.setAcctNo(spdBankBiSafeConfig.getAcctNo());
        accountDetailReqBody.setBeginDate(startDate.format(DateTimeFormatter.ofPattern(DatePattern.PURE_DATE_PATTERN)));
        accountDetailReqBody.setEndDate(endDate.format(DateTimeFormatter.ofPattern(DatePattern.PURE_DATE_PATTERN)));
        accountDetailReqBody.setQueryNumber(mixRsltNum);
        accountDetailReqBody.setBeginNumber(startRow < 1 ? 1 : startRow);
        accountDetailReqDTO.setHead(baseSpdBankHead);
        accountDetailReqDTO.setClearBody(accountDetailReqBody);

//        ParentAccountDetailReqDTO reqDTO = new ParentAccountDetailReqDTO();
//        reqDTO.setAreaCode(spdBankConfig.getAreaCode());
//        reqDTO.setSettleAcctNo(spdBankConfig.getSettleAcctNo());
//        reqDTO.setStartDate(startDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")));
//        reqDTO.setEndDate(endDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")));
//        reqDTO.setStrtRslt(String.valueOf(startRow < 1 ? 1 : startRow));
//        reqDTO.setTtlRquestRsltNum(String.valueOf(mixRsltNum));

        try {
            AccountDetailRespDTO respDTO = this.sendSPDBBiSafe(accountDetailReqDTO);
            if (respDTO == null || respDTO.getHead() == null) {
                throw new RuntimeException("账户响应报文为空");
            }
            if (("AAAAAAA").equals(respDTO.getHead().getReturnCode()) && respDTO.getClearBody() != null) {
//                Integer totalCount = respDTO.getClearBody().getTotalCount();
                Integer currentRespNum = respDTO.getClearBody().getLists().size();
                //单次查询返回的记录数=返回结果记录数，则可能后面还有记录所以需要再次查询
                if (currentRespNum.equals(mixRsltNum)) {
                    Integer nextStartRow = startRow + mixRsltNum;
                    // 查询有限流，需要根据实际尽量触发限流，暂休眠1s
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        ElgLogger.error(log, l -> l.error("查询有限流，需要根据实际尽量触发限流，暂休眠1s。异常:{}", e.getMessage(), e));
                    }
                    processAcctTranDtlInfo(respDTO.getClearBody());
                    return getAccountDetail(nextStartRow, startDate, endDate);
                } else {
                    processAcctTranDtlInfo(respDTO.getClearBody());
                    return true;
                }
            } else {
                throw new RuntimeException("账户响应报文状态码非成功[" + respDTO.getHead().getReturnCode() + ":" + respDTO.getHead().getReturnMsg() + "]");
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void processAcctTranDtlInfo(AccountDetailRespBody accountDetailRespBody) {
        accountDetailRespBody.getLists().stream()
                .filter(o -> "1".equals(o.getTranFlag()))
                .filter(o -> {
                    boolean exists = spdBankNoticeService.existsCoreSerNo(o.getTransDate() + o.getSeqNo());
                    log.info("账户明细核心流水号[{}]已存在，忽略该笔明细处理.", o.getTransDate() + o.getSeqNo());
                    return !exists;
                }).map(o -> this.buildSPdBankNotice(accountDetailRespBody, o)).forEach(o -> {
            try {
                saveAndAutoAuditWaitPayConfirm(o);
            } catch (Exception e) {
                ElgLogger.error(log, l -> l.error(e.getMessage(), e));
            }

        });

    }

    @Transactional(rollbackFor = Exception.class)
    public void saveAndAutoAuditWaitPayConfirm(SpdBankNotice spdBankNotice) {
        try {
            SpdBankNotice dbSpdBankNotice = spdBankNoticeService.saveOrUpdate(spdBankNotice);
            acceptOrderInfoService.autoAuditWaitPayConfirm(dbSpdBankNotice);
        } catch (Exception e) {
            throw new RuntimeException("拉取账户明细核查受理订单到账异常", e);
        }
    }

    /**
     * 核心流水号唯一：来账账号+交易金额+附言包含的标段编号的定位某笔受理订单号到账证明
     */
    public SpdBankNotice buildSPdBankNotice(AccountDetailRespBody accountDetailRespBody, AccountDetailHistoryList accountDetail) {

        String noticeType = getNoticeTypeByBookAcctType(accountDetail.getPayeeAcctType());
        SpdBankNotice spdBankNotice = new SpdBankNotice();
        spdBankNotice.setId(IdGenerate.getId());
        spdBankNotice.setNoticeDate(accountDetail.getTransDate());
        spdBankNotice.setNoticeTime(accountDetail.getTransTime());
        spdBankNotice.setNoticeType(noticeType);
        spdBankNotice.setNoticeSerNo(accountDetail.getTransDate() + accountDetail.getSeqNo());
        spdBankNotice.setZoneCode("");
        spdBankNotice.setSecuNo("");
        spdBankNotice.setSecuName("");
        spdBankNotice.setMerchNo("");
        spdBankNotice.setMerchName("");
        spdBankNotice.setParentAcctNo(accountDetailRespBody.getAcctNo());
        spdBankNotice.setParentAcctName(accountDetailRespBody.getAcctName());
        spdBankNotice.setDepSerNo(accountDetail.getTransDate() + accountDetail.getSeqNo());
        spdBankNotice.setCoreSerNo(accountDetail.getTransDate() + accountDetail.getSeqNo());
        spdBankNotice.setCoreTransDate(accountDetail.getTransDate());
        spdBankNotice.setCoreTransTime(accountDetail.getTransTime());
        spdBankNotice.setTransDate(accountDetail.getTransDate());
        spdBankNotice.setTransTime(accountDetail.getTransTime());
        //这里存储对方账号类型
        spdBankNotice.setBusiType(accountDetail.getPayeeAcctType());
        spdBankNotice.setTransAmt(accountDetail.getTxAmount());
        spdBankNotice.setAcctBankName(accountDetail.getPayeeBankName());
        spdBankNotice.setAcctBankNo(accountDetail.getPayeeBankNo());
        spdBankNotice.setAcctName(accountDetail.getPayeeName());
        spdBankNotice.setAcctNo(accountDetail.getPayeeAcctNo());
        spdBankNotice.setAttach(accountDetail.getNote());
        spdBankNotice.setRemark1(null);
        spdBankNotice.setRemark2(null);
        spdBankNotice.setRemark3(null);
        spdBankNotice.setGmtCreate(new Date());
        spdBankNotice.setGmtModified(new Date());

        return spdBankNotice;
    }

    /**
     * 账户类型 string(2)
     * 00-客户入金 ==>客户入金通知	A1007
     * 01-非客户入金 非客户入金通知	A1010
     * 02-不明入金 不明入金通知	A1008
     * 03-客户出金 客户出金通知	A1004
     * 04-非客户出金 非客户出金通知	A1003
     * 05-调拨入金 内部资金调拨通知	A3003
     * 06-调拨出金 内部资金调拨通知	A3003
     */
    private static String getNoticeTypeByBookAcctType(String bookAcctType) {
        switch (bookAcctType) {
            case "00":
                return "A1007";
            case "01":
                return "A1010";
            case "02":
                return "A1008";
            case "03":
                return "A1004";
            case "04":
                return "A1003";
            case "05":
                return "A3003";
            case "06":
                return "A3003";
            default:
                return null;
        }
    }
}
